package com.heima.schedule.service;

import com.alibaba.fastjson.JSON;
import com.heima.common.cache.CacheService;
import com.heima.model.schedule.dto.Task;
import com.heima.model.schedule.pojos.Taskinfo;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
@Slf4j
public class TaskServiceImpl implements TaskService{
    @Autowired
    private TaskinfoService taskinfoService;

    @Autowired
    private TaskinfoLogsService taskinfoLogsService;

    @Autowired
    private CacheService cacheService;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Long addTask(Task task) {
        //判断Task是否为空
        if (task ==null){
            log.warn("task不能为空");
            return null ;
        }
        //1.插入Taskinfo表
        Taskinfo taskinfo = new Taskinfo();
        //复制
        BeanUtils.copyProperties(task,taskinfo);
        taskinfo.setExecuteTime(new Date(task.getExecuteTime()));
        boolean saveResult = taskinfoService.save(taskinfo);
        if (!saveResult){
            log.warn("任务插入失败");
            return null;
        }
        //2.插入Taskinfoconf表
        TaskinfoLogs taskinfoLogs =new TaskinfoLogs();
        BeanUtils.copyProperties(task,taskinfoLogs);
        taskinfoLogs.setExecuteTime(new Date(task.getExecuteTime()));
        taskinfoLogs.setStatus(0);

        boolean logSaveResult = taskinfoLogsService.save(taskinfoLogs);
        if (!logSaveResult){
            log.warn("日志插入失败");
            throw new RuntimeException("日志插入失败");
        }
        //3.执行时间是否小于当前时间
        long executeTime = task.getExecuteTime();
        long currentTime = System.currentTimeMillis();
        if (executeTime<=currentTime){
            cacheService.lLeftPush("TOPIC", JSON.toJSONString(task));
        }
        //4.执行时间是否小于未来五分钟
        long futureTime = currentTime+( 5*60*1000);
        //5.返回id
        if (executeTime <=futureTime){
            cacheService.zAdd("FUTURE",JSON.toJSONString(task),executeTime);
        }
        return taskinfo.getTaskId();
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean cancelTask(Long taskId) {

        //1.删除task_info
        boolean removeById = taskinfoService.removeById(taskId);
        if (!removeById){
            log.warn("taskId不能为空");
            return false;
        }
        //2.更新task_info_log
        //获得taskinfolog  id
        TaskinfoLogs logs = taskinfoLogsService.getById(taskId);
        logs.setStatus(2);
        //更新
        boolean updateById = taskinfoLogsService.updateById(logs);
        if (!updateById){
            log.warn("任务更新失败");
            throw new RuntimeException("任务更新失败");
        }
        //3.获得task_对象
        Task task = new Task();
        BeanUtils.copyProperties(logs,task);
       cacheService.lRemove("TOPIC",0,JSON.toJSONString(task));
       cacheService.zRemove("FUTURE",JSON.toJSONString(task));
       //返回
        return true;
    }

    //消费
    @Override
    public Boolean pollTask() {
        //指定topic队列，进行消费
        String topic = cacheService.lRightPop("TOPIC");
        if (StringUtils.isEmpty(topic)){
            return false;
        }
        Task task = JSON.parseObject(topic, Task.class);
        if (task ==null){
            log.warn("没有可执行任务");
            return false;
        }
        //任务id


        //删除
        boolean removeById = taskinfoService.removeById(task.getTaskId());
        if (!removeById){
            return false;
        }
        Long executeTime = task.getExecuteTime();
        TaskinfoLogs logs = new TaskinfoLogs();
        logs.setExecuteTime(new Date(executeTime));
        BeanUtils.copyProperties(task,logs);
        logs.setStatus(1);
        boolean logResult = taskinfoLogsService.updateById(logs);
        if (!logResult ){
            log.warn("日志更新失败");
            throw  new RuntimeException("日志更新失败");
        }


        //把json字符串转成task
        return true;
    }
    //定时刷新
    @Scheduled(fixedRate = 5 *60 *1000)
    public void refresh(){
        String token = cacheService.tryLock("FUTURE_TASK_SYNC", 1000 * 30);
        if (StringUtils.isEmpty(token)){
            return;
        }


        Set<String> tasks = cacheService.zRangeByScore("FUTURE", 0, System.currentTimeMillis());
        if (!tasks.isEmpty()){
            cacheService.refreshWithPipeline("FUTURE","TOPIC",tasks);

        }
    }
    //初始化数据
    @Scheduled(fixedRate = 5 * 60 * 1000)
    public void initDate(){
        //清除redis中的数据
        clear();
        //读取mysql中的任务(list)
        List<Taskinfo> list = taskinfoService.list();
        if (CollectionUtils.isEmpty(list)){
            log.warn("任务为空");
            return;
        }
        //遍历list
        //判断
        for (Taskinfo taskinfo : list) {
            if (taskinfo == null ){
                continue;
            }
            //获取三个时间，过去，现在，未来
            Long executeTime = taskinfo.getExecuteTime().getTime();//过去
            Long currentTime = System.currentTimeMillis();//现在
            Long futureTime = currentTime+(5*60*1000);//未来五分钟

            Task task = new Task();
            //将task中的数据copy到taskInfo
            BeanUtils.copyProperties(taskinfo,task);
            task.setExecuteTime(executeTime);

            //判断
            //执行时间小于现在时间
            if (executeTime <= currentTime){
                cacheService.lLeftPush("TOPIC",JSON.toJSONString(task));
                //执行时间小于未来时间
            }else if (executeTime <= futureTime){
                cacheService.zAdd("FUTURE",JSON.toJSONString(task),executeTime);

            }

        }
    }
    public void clear(){
        cacheService.delete("TOPIC");
        cacheService.delete("FUTURE");
    }

}
